require "osx/cocoa"

class Float
  DPI = 72 # TODO: get this magic number from system if possible
  CM_PER_IN = 2.54 # 2.54cm = 1in

  def to_cm
    self / DPI * CM_PER_IN
  end

  def to_point
    self * DPI / CM_PER_IN
  end
end

# NOTE: Most of the business logic is in the view class. I don't like that, but it is an easy implementation of Cocoa applications.
# TODO: To be more sophisticated design of implementation...
class MyContentView <  OSX::NSView
  # TODO: use kPDFDisplayBoxMediaBox enum if possible
  PDFDisplayBoxMediaBox = 0

  ib_outlets :left_margin, :bottom_margin
  ib_outlets :left_margin_min, :bottom_margin_min
  ib_outlets :borders

  ib_action :applyMargins
  ib_action :rotate
  ib_action :toggleBorder

  def initWithFrame(frame)
    super_initWithFrame(frame)
    return self
  end

  def awakeFromNib
    @angle = 0
  end

  def resetMargins(newPrintInfo=nil)
    print_info = newPrintInfo || printInfo
    imgableSize = printInfo.imageablePageBounds.size

    @cols = (imgableSize.width / srcSize.width).to_i
    @rows = (imgableSize.height / srcSize.height).to_i

    # TODO: removing margins are not needed every time
    print_info.leftMargin = 0
    print_info.rightMargin = 0
    print_info.topMargin = 0
    print_info.bottomMargin = 0

    @left_margin.doubleValue = (print_info.imageablePageBounds.origin.x + print_info.leftMargin).to_cm
    @left_margin_min.stringValue = "(>= #{'%.2f' % print_info.imageablePageBounds.origin.x.to_cm})"
    @bottom_margin.doubleValue = print_info.imageablePageBounds.origin.y.to_cm
    @bottom_margin_min.stringValue = "(>= #{'%.2f' % @bottom_margin.doubleValue})"
    self.needsDisplay = true
  end

  def applyMargins(sender)
    self.needsDisplay = true
  end

  def rotate(sender)
    @angle = sender.intValue * 90
    resetMargins
    self.needsDisplay = true
  end

  def toggleBorder(sender)
    self.needsDisplay = true
  end

  def drawRect(rect)
    # set frame save as paper size
    self.frameSize = printInfo.paperSize

    # getting source pdf and it's size 
    doc = self.window.windowController.document.pdf
    page = doc.pageAtIndex(0)

    # draw paper on the screen
    if NSGraphicsContext.currentContextDrawingToScreen
      #NSColor.lightGrayColor.setFill
      NSColor.whiteColor.setFill
      NSBezierPath.fillRect(NSRect.new(NSPoint.new, printInfo.paperSize))
    end
    
    saveGraphicsState do |transform| #
      transform.translateXBy_yBy(@left_margin.doubleValue.to_point, @bottom_margin.doubleValue.to_point)
      transform.concat

      # rendering src PDF multiple times
      @rows.times do |y|
        @cols.times do |x|
          saveGraphicsState do |transform|
            # translate
            transform.translateXBy_yBy((srcSize.width + 1) * x, (srcSize.height + 1) * y)
            # rotate
            if @angle != 0
              transform.translateXBy_yBy(srcSize.width, 0)
              transform.rotateByDegrees(@angle)
            end
            transform.concat
            # render pdf page
            page.drawWithBox(PDFDisplayBoxMediaBox)
            # border
            if @borders.intValue == 1
              NSColor.colorWithDeviceRed_green_blue_alpha(0.8,0.8,0.8,1).set
              NSBezierPath.strokeRect(NSRect.new(NSPoint.new, NSSize.new(srcSize(0).width + 1, srcSize(0).height + 1)))
            end
          end
        end
      end
    end
  end

  private

  def dstSize
    dstSize = printInfo.imageablePageBounds.size
    dstSize.width -= printInfo.leftMargin + printInfo.rightMargin
    dstSize.height -= printInfo.topMargin + printInfo.bottomMargin
    return dstSize
  end

  def srcSize(angle=@angle)
    size = pdfPage.boundsForBox(PDFDisplayBoxMediaBox).size
    case angle
    when 0
      NSSize.new(size.width, size.height)
    when 90
      NSSize.new(size.height, size.width)
    end
  end

  def printInfo
    self.window.windowController.document.printInfo
  end

  def pdfPage
    self.window.windowController.document.pdf.pageAtIndex(0)
  end

  # gaining the power of ruby's block
  def saveGraphicsState(&block)
    NSGraphicsContext.saveGraphicsState
    transform = NSAffineTransform.transform
    yield transform
    NSGraphicsContext.restoreGraphicsState
  end
end
